"""
通用工具函数模块
提供各种辅助函数
"""
import os
import re
import uuid
import random
import string
from datetime import datetime, timedelta
from typing import Any, List, Dict, Optional, Union
from urllib.parse import urlparse, urljoin
from html import escape

from flask import request, url_for
from markdown import markdown
from bleach import clean, linkify

def generate_user_id() -> str:
    """
    生成8位数字用户ID
    
    Returns:
        str: 8位数字ID
    """
    return str(random.randint(10000000, 99999999))

def format_timestamp(timestamp: Optional[datetime] = None, 
                    format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
    """
    格式化时间戳
    
    Args:
        timestamp: 时间戳，默认为当前时间
        format_str: 格式字符串
        
    Returns:
        str: 格式化后的时间字符串
    """
    if timestamp is None:
        timestamp = datetime.utcnow()
    
    if isinstance(timestamp, str):
        try:
            timestamp = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
        except ValueError:
            return timestamp
    
    return timestamp.strftime(format_str)

def relative_time(timestamp: datetime) -> str:
    """
    生成相对时间描述（如"2小时前"）
    
    Args:
        timestamp: 时间戳
        
    Returns:
        str: 相对时间描述
    """
    now = datetime.utcnow()
    diff = now - timestamp
    
    if diff < timedelta(minutes=1):
        return '刚刚'
    elif diff < timedelta(hours=1):
        minutes = int(diff.total_seconds() / 60)
        return f'{minutes}分钟前'
    elif diff < timedelta(days=1):
        hours = int(diff.total_seconds() / 3600)
        return f'{hours}小时前'
    elif diff < timedelta(days=30):
        days = diff.days
        return f'{days}天前'
    elif diff < timedelta(days=365):
        months = int(diff.days / 30)
        return f'{months}个月前'
    else:
        years = int(diff.days / 365)
        return f'{years}年前'

def sanitize_html(html_content: str, allow_markdown: bool = True) -> str:
    """
    清理HTML内容，防止XSS攻击
    
    Args:
        html_content: HTML内容
        allow_markdown: 是否允许Markdown
        
    Returns:
        str: 清理后的HTML
    """
    if not html_content:
        return ''
    
    # 如果允许Markdown，先转换为HTML
    if allow_markdown:
        html_content = markdown(html_content)
    
    # 允许的HTML标签
    allowed_tags = [
        'p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre', 
        'blockquote', 'ul', 'ol', 'li', 'a', 'img', 'span',
        'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div'
    ]
    
    # 允许的HTML属性
    allowed_attributes = {
        'a': ['href', 'title', 'target', 'rel'],
        'img': ['src', 'alt', 'title', 'width', 'height'],
        'span': ['class', 'style'],
        'div': ['class', 'style'],
        'code': ['class'],
        'pre': ['class']
    }
    
    # 清理HTML
    cleaned_html = clean(
        html_content,
        tags=allowed_tags,
        attributes=allowed_attributes,
        protocols=['http', 'https', 'mailto'],
        strip=True,
        strip_comments=True
    )
    
    # 自动链接URL
    cleaned_html = linkify(cleaned_html)
    
    return cleaned_html

def validate_email(email: str) -> bool:
    """
    验证邮箱格式
    
    Args:
        email: 邮箱地址
        
    Returns:
        bool: 是否有效
    """
    if not email or not isinstance(email, str):
        return False
    
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None

def validate_username(username: str) -> bool:
    """
    验证用户名格式
    
    Args:
        username: 用户名
        
    Returns:
        bool: 是否有效
    """
    if not username or not isinstance(username, str):
        return False
    
    # 用户名规则：3-20个字符，只能包含字母、数字、下划线和连字符
    pattern = r'^[a-zA-Z0-9_-]{3,20}$'
    return re.match(pattern, username) is not None

def get_file_extension(filename: str) -> str:
    """
    获取文件扩展名
    
    Args:
        filename: 文件名
        
    Returns:
        str: 文件扩展名（小写，不带点）
    """
    if not filename:
        return ''
    
    return os.path.splitext(filename)[1].lower().lstrip('.')

def is_allowed_file(filename: str, allowed_extensions: List[str]) -> bool:
    """
    检查文件类型是否允许
    
    Args:
        filename: 文件名
        allowed_extensions: 允许的扩展名列表
        
    Returns:
        bool: 是否允许
    """
    if not filename:
        return False
    
    ext = get_file_extension(filename)
    return ext in allowed_extensions

def generate_filename(original_filename: str, prefix: str = '') -> str:
    """
    生成安全的文件名
    
    Args:
        original_filename: 原始文件名
        prefix: 文件名前缀
        
    Returns:
        str: 生成的文件名
    """
    # 提取扩展名
    ext = get_file_extension(original_filename)
    
    # 生成唯一ID
    unique_id = uuid.uuid4().hex
    
    # 构建文件名
    if prefix:
        filename = f"{prefix}_{unique_id}"
    else:
        filename = unique_id
    
    if ext:
        filename += f".{ext}"
    
    return filename

def paginate_query(query, page: int, per_page: int = 20):
    """
    分页查询工具函数
    
    Args:
        query: SQLAlchemy查询对象
        page: 页码
        per_page: 每页数量
        
    Returns:
        Pagination: 分页对象
    """
    return query.paginate(page=page, per_page=per_page, error_out=False)

def truncate_text(text: str, max_length: int = 100, suffix: str = '...') -> str:
    """
    截断文本
    
    Args:
        text: 原始文本
        max_length: 最大长度
        suffix: 后缀
        
    Returns:
        str: 截断后的文本
    """
    if not text or len(text) <= max_length:
        return text
    
    return text[:max_length - len(suffix)] + suffix

def generate_random_string(length: int = 8, 
                          include_digits: bool = True,
                          include_letters: bool = True,
                          include_special: bool = False) -> str:
    """
    生成随机字符串
    
    Args:
        length: 长度
        include_digits: 包含数字
        include_letters: 包含字母
        include_special: 包含特殊字符
        
    Returns:
        str: 随机字符串
    """
    characters = ''
    
    if include_digits:
        characters += string.digits
    if include_letters:
        characters += string.ascii_letters
    if include_special:
        characters += '!@#$%^&*()_+-=[]{}|;:,.<>?'
    
    if not characters:
        characters = string.ascii_letters + string.digits
    
    return ''.join(random.choice(characters) for _ in range(length))

def get_client_ip() -> str:
    """
    获取客户端IP地址
    
    Returns:
        str: IP地址
    """
    if request.headers.get('X-Forwarded-For'):
        return request.headers.get('X-Forwarded-For').split(',')[0].strip()
    elif request.headers.get('X-Real-IP'):
        return request.headers.get('X-Real-IP')
    else:
        return request.remote_addr

def format_file_size(size_bytes: int) -> str:
    """
    格式化文件大小
    
    Args:
        size_bytes: 字节数
        
    Returns:
        str: 格式化后的大小
    """
    if size_bytes == 0:
        return '0 B'
    
    size_names = ['B', 'KB', 'MB', 'GB', 'TB']
    i = 0
    while size_bytes >= 1024 and i < len(size_names) - 1:
        size_bytes /= 1024.0
        i += 1
    
    return f'{size_bytes:.2f} {size_names[i]}'

def parse_bool(value: Any) -> bool:
    """
    解析布尔值
    
    Args:
        value: 输入值
        
    Returns:
        bool: 解析后的布尔值
    """
    if isinstance(value, bool):
        return value
    
    if isinstance(value, (int, float)):
        return bool(value)
    
    if isinstance(value, str):
        value = value.lower().strip()
        if value in ('true', 'yes', '1', 'on', 'y'):
            return True
        elif value in ('false', 'no', '0', 'off', 'n'):
            return False
    
    return False

def safe_int(value: Any, default: int = 0) -> int:
    """
    安全转换为整数
    
    Args:
        value: 输入值
        default: 默认值
        
    Returns:
        int: 转换后的整数
    """
    try:
        return int(value)
    except (ValueError, TypeError):
        return default

def safe_str(value: Any, default: str = '') -> str:
    """
    安全转换为字符串
    
    Args:
        value: 输入值
        default: 默认值
        
    Returns:
        str: 转换后的字符串
    """
    if value is None:
        return default
    
    try:
        return str(value).strip()
    except (ValueError, TypeError):
        return default

def pluck(items: List[Dict], key: str) -> List:
    """
    从字典列表中提取指定键的值
    
    Args:
        items: 字典列表
        key: 键名
        
    Returns:
        list: 值列表
    """
    return [item.get(key) for item in items if key in item]

def group_by(items: List[Dict], key: str) -> Dict[Any, List]:
    """
    按指定键对字典列表进行分组
    
    Args:
        items: 字典列表
        key: 分组键名
        
    Returns:
        dict: 分组后的字典
    """
    grouped = {}
    for item in items:
        group_key = item.get(key)
        if group_key not in grouped:
            grouped[group_key] = []
        grouped[group_key].append(item)
    return grouped

def is_safe_url(target_url: str) -> bool:
    """
    检查URL是否安全（防止开放重定向）
    
    Args:
        target_url: 目标URL
        
    Returns:
        bool: 是否安全
    """
    if not target_url:
        return False
    
    ref_url = urlparse(request.host_url)
    test_url = urlparse(urljoin(request.host_url, target_url))
    
    return test_url.scheme in ('http', 'https') and \
           ref_url.netloc == test_url.netloc

def generate_gravatar_url(email: str, size: int = 80, default: str = 'identicon') -> str:
    """
    生成Gravatar头像URL
    
    Args:
        email: 邮箱地址
        size: 头像尺寸
        default: 默认头像类型
        
    Returns:
        str: Gravatar URL
    """
    import hashlib
    
    email_hash = hashlib.md5(email.lower().encode('utf-8')).hexdigest()
    return f"https://www.gravatar.com/avatar/{email_hash}?s={size}&d={default}"

def format_number(number: int) -> str:
    """
    格式化数字（如：1.2K, 3.4M）
    
    Args:
        number: 数字
        
    Returns:
        str: 格式化后的数字
    """
    if number < 1000:
        return str(number)
    elif number < 1000000:
        return f'{number/1000:.1f}K'
    else:
        return f'{number/1000000:.1f}M'

def humanize_list(items: List[str], conjunction: str = '和') -> str:
    """
    将列表人性化显示（如：["A", "B", "C"] -> "A、B和C"）
    
    Args:
        items: 字符串列表
        conjunction: 连接词
        
    Returns:
        str: 人性化显示的字符串
    """
    if not items:
        return ''
    elif len(items) == 1:
        return items[0]
    elif len(items) == 2:
        return f'{items[0]}{conjunction}{items[1]}'
    else:
        return f'{", ".join(items[:-1])}、{conjunction}{items[-1]}'

def get_current_year() -> int:
    """
    获取当前年份
    
    Returns:
        int: 当前年份
    """
    return datetime.now().year

def generate_breadcrumbs(path: str, base_url: str = '') -> List[Dict[str, str]]:
    """
    生成面包屑导航数据
    
    Args:
        path: 路径
        base_url: 基础URL
        
    Returns:
        list: 面包屑列表
    """
    breadcrumbs = []
    parts = path.strip('/').split('/')
    
    current_path = ''
    for i, part in enumerate(parts):
        current_path += f'/{part}'
        is_last = i == len(parts) - 1
        
        breadcrumbs.append({
            'name': part.replace('_', ' ').title(),
            'url': urljoin(base_url, current_path) if not is_last else None,
            'is_active': is_last
        })
    
    return breadcrumbs

def calculate_read_time(text: str, words_per_minute: int = 200) -> int:
    """
    计算阅读时间（分钟）
    
    Args:
        text: 文本内容
        words_per_minute: 每分钟阅读字数
        
    Returns:
        int: 阅读时间（分钟）
    """
    if not text:
        return 0
    
    # 简单估算字数（中英文混合）
    word_count = len(text.strip().split())
    read_time = max(1, round(word_count / words_per_minute))
    
    return read_time

def generate_excerpt(text: str, max_length: int = 150, strip_html: bool = True) -> str:
    """
    生成摘要
    
    Args:
        text: 原始文本
        max_length: 最大长度
        strip_html: 是否去除HTML标签
        
    Returns:
        str: 摘要
    """
    if not text:
        return ''
    
    # 去除HTML标签
    if strip_html:
        import re
        text = re.sub(r'<[^>]+>', '', text)
    
    # 去除多余空白
    text = ' '.join(text.split())
    
    # 截断
    return truncate_text(text, max_length)